/*************************************************************************
        > File Name: dataitem.cpp
        > Author: xuwenlong
        > Mail: myxuan475@126.com
        > Created Time: 2018年02月24日 星期六 16时00分30秒
 ************************************************************************/
#include "dataitem.h"
#include "express.h"
#include "tempbuf.h"
#include <stdio.h>
#include <utils/custom.h>

namespace libxcom {

DataItem::~DataItem()
{
    listValues_.clear();
    listExprs_.clear();
}

DataItem::DataItem(const std::string &name, const std::string &templ, const std::string &expr, float rate) :
    name_(name), operator_(this), fRate_(rate)
{
    template_.Parse(templ);
    dataExpr_ = wrapExpress(expr);
}

bool DataItem::IsEmpty()
{
    if (name_.empty() || listExprs_.empty() || listValues_.empty() || dataExpr_ == nullptr)
        return true;
    return false;
}

void DataItem::ExprToParams(const vector<OpValue> &params)
{
    listValues_.clear();

    ParamsLock palock(operator_);
    operator_.AddtoStack(Operator::AUTO_PARAM, params);
    for (vector<std::shared_ptr<Express>>::iterator iter = listExprs_.begin(); iter != listExprs_.end(); ++iter) {
        std::shared_ptr<Express> expr = *iter;
        if (expr == nullptr) {
            HTELINK_LOG_ERR("params expr is null");
            listValues_.push_back(OpValue::Null());
            continue;
        }
        listValues_.push_back(expr->GenCmd(operator_));
    }
}

void DataItem::ParseDataExpr(const string &expr, float rate)
{
    fRate_ = rate;
    dataExpr_ = wrapExpress(expr);
}

void DataItem::EmptyParam()
{
    listExprs_.clear();
    listValues_.clear();
}

bool DataItem::PushParam(const std::string &param)
{
    std::shared_ptr<Express> expr = wrapExpress(param);
    if (expr == nullptr) {
        return false;
    }

    listValues_.push_back(expr->GenCmd(operator_));
    listExprs_.push_back(expr);
    return true;
}

bool DataItem::PushParam(const OpValue &ov)
{
    listValues_.push_back(ov);
    return true;
}

std::shared_ptr<Express> DataItem::LocateFunction(const string &def, int funcId)
{
    if (!template_.IsParse())
        return nullptr;
    return template_.LocateFunction(def.c_str(), funcId);
}

void DataItem::SetVar(const std::string &var, const std::string &expr)
{
    std::shared_ptr<Express> express = wrapExpress(expr);
    if (express == nullptr) {
        HTELINK_LOG_WARN("%s format error", expr.c_str());
        return;
    }
    Operator oper(nullptr);
    OpValue value = express->GenCmd(oper);
    if (value.IsEmpty()) {
        HTELINK_LOG_ERR("%s gen cmd error", expr.c_str());
        return;
    }
    SetVar(var, value);
}

OpValue DataItem::GetResult(const std::string &expr, float rate)
{
    std::shared_ptr<Express> dataExpr = wrapExpress(expr);
    if (dataExpr == nullptr) {
        return OpValue::Null();
    }
    OpValue result = dataExpr->ParseCmd(operator_);
    return RateCall(result, rate);
}

RECV_RESULT DataItem::ParseCmd(OpValue &result, const CmdValue &recvCmd, const std::string &func, int32_t funcId)
{
    if (recvCmd.IsEmpty())
        return RECV_NOT_AVAILIABLE;

    ParamsLock palock(operator_);
    operator_.AddtoStack(Operator::AUTO_PARAM, listValues_);
    std::shared_ptr<Express> expr = LocateFunction(func, funcId);

    if (expr == nullptr) {
        HTELINK_LOG_ERR("express is invalid");
        return RECV_NOT_AVAILIABLE;
    }

    result = expr->ParseCmd(operator_, 0, recvCmd);

    if (result.IsEmpty()) {
        return RECV_NOT_AVAILIABLE;
    }
    // 校验
    OpValue chk = operator_["chk"];
    if (chk.IsEmpty())
        return RECV_COMPLETE;
    if (chk.Integer() == 0)
        return RECV_NOT_AVAILIABLE;
    return RECV_COMPLETE;
}

OpValue DataItem::GenerateCmd(const std::string &func)
{
    ParamsLock palock(operator_);
    operator_.AddtoStack(Operator::AUTO_PARAM, listValues_);
    std::shared_ptr<Express> expr = LocateFunction(func, -1);
    if (expr == nullptr) {
        HTELINK_LOG_ERR("express is invalid");
        return CmdValue::Null();
    }
    return expr->GenCmd(operator_);
}

RECV_RESULT DataItem::ParseRecvCmd(const CmdValue &recvCmd, const std::string &func, int32_t funcId)
{
    OpValue result;
    return ParseCmd(result, recvCmd, func, funcId);
}

OpValue DataItem::Result()
{
    std::shared_ptr<Express> expr = dataExpr_;
    if (expr == nullptr)
        return OpValue::Null();
#ifdef LOCAL_TEST
    return 23.77;
#endif
    OpValue result = expr->ParseCmd(operator_);
    return RateCall(result, fRate_);
}

void DataItem::ResetCmd()
{
    operator_.clearOut();
}

TYPE_DEFINE DataItem::DataType()
{
    if (!floatcmp(fRate_, 1))
        return TYPE_F32;
    if (dataExpr_ == nullptr)
        return TYPE_CMD;
    return dataExpr_->ResultType(operator_);
}

OpValue DataItem::FuncCall()
{
    ParamsLock palock(operator_);
    operator_.AddtoStack(Operator::AUTO_PARAM, listValues_);

    std::shared_ptr<Express> expr = dataExpr_;
    if (expr == nullptr)
        return OpValue::Null();
    OpValue result = expr->ParseCmd(operator_);
    if (result.IsEmpty())
        return result;
    return RateCall(result, fRate_);
}

OpValue DataItem::RateCall(const OpValue &result, float rate)
{
    float f = rate;
    switch (result.type) {
        case TYPE_S8:
        case TYPE_U8:
        case TYPE_S16:
        case TYPE_U16:
        case TYPE_S32:
        case TYPE_U32:
        case TYPE_S64:
        case TYPE_U64: {
            if (floatcmp(f, 1))
                return result;
            else
                return (OpValue &) result / f;
        }
        case TYPE_F32:
        case TYPE_F64:
            return (OpValue &) result / f;
        default:
            return result;
    }
}

std::shared_ptr<Express> DataItem::wrapExpress(const std::string &str)
{
    TempBuf tempBuf(str);
    int32_t pos = 0;
    TempBuf::RESULT res = tempBuf.Parse(pos, true);
    if ((res != TempBuf::OK && res != TempBuf::FIN))
        return NULL;
    return tempBuf.ResultExpress();
}
} // namespace libxcom
